#include <iostream>
#include <queue>
#include <pthread.h>
#include <unistd.h>
#include <ctime>
#include "lockGuard.hpp"

using namespace std;
#define defaultCap 5

template <class T> // 可以存放用户定义的数据类型
class BlockingQueue
{
public:
    BlockingQueue(int capacity = defaultCap) : capacity_(capacity)
    {
        pthread_mutex_init(&mtx, NULL);
        pthread_cond_init(&isEmpty, NULL);
        pthread_cond_init(&isFull, NULL); // 初始化
    }
    ~BlockingQueue()
    {
        pthread_mutex_destroy(&mtx);
        pthread_cond_destroy(&isEmpty);
        pthread_cond_destroy(&isFull); // 销毁
    }

    bool isBqEmpty() { return bq.size() == 0; }
    bool isBqFull() { return bq.size() == capacity_; } // 判断阻塞队列为空还是为满

    void Push(const T &obj) // 生产者生产一个数据
    {
        // pthread_mutex_lock(&mtx); // 解锁
        lockGuard lock(&mtx);

        while (isBqFull())
        {
            pthread_cond_wait(&isFull, &mtx); // 没有可放数据的位置，生产者等待被唤醒
        }
        // 获得锁且资源就绪
        bq.push(obj);
        pthread_cond_signal(&isEmpty); // 通知消费者

        // pthread_mutex_unlock(&mtx); // 解锁
    }

    void Pop(T &obj) // 消费者消费一个数据
    {
        // pthread_mutex_lock(&mtx); // 加锁
        lockGuard lock(&mtx);

        while (isBqEmpty())
        {
            pthread_cond_wait(&isEmpty, &mtx); // 没有数据，消费者等待被唤醒
        }

        // 获得锁且资源就绪
        obj = bq.front();
        bq.pop();
        pthread_cond_signal(&isFull); // 通知生产者

        // pthread_mutex_unlock(&mtx); // 解锁
    }

private:
    queue<T> bq;            // 存放数据的队列
    int capacity_;          // 阻塞队列容量上限
    pthread_mutex_t mtx;    // 对队列加锁，所有生产者消费者之间都是互斥关系
    pthread_cond_t isEmpty; // 队列空了，消费者在这个条件变量下阻塞
    pthread_cond_t isFull;  // 队列满了，生产者在这个条件变量下阻塞
};